package io.github.wppli.domain.recommond.rservice;

import cc.jq1024.middleware.redisson.IRedissonService;
import io.github.wppli.domain.recommond.model.entity.RecommendUserEntity;
import io.github.wppli.domain.user.repository.IUserRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author li--jiaqiang 2025−03−14
 */
@Slf4j
@AllArgsConstructor
public abstract class AbstractUserRecommendService {

    protected final IRedissonService redissonService;
    protected final IUserRepository userRepository;

    /**
     * 获取用户推荐实体
     * @param itemId        目标物品ID(被关注用户ID) followee_id
     * @param recommendNum 推荐数量
     * @return 推荐实体
     * @throws Exception 异常
     */
    public List<RecommendUserEntity> getRecommendEntityByItemCF(Long itemId, int recommendNum) throws Exception {
        if (itemId == null) {
            throw new IllegalArgumentException("itemId must not be null");
        }
        if (recommendNum <= 0) {
            throw new IllegalArgumentException("recommendNum must be greater than 0");
        }
        // 从缓存中获取推荐商品
        String key =  "userId_" + itemId + "_" + recommendNum;
        List<RecommendUserEntity> recommendByItemFromCache = redissonService.getList(key, RecommendUserEntity.class);
        if (recommendByItemFromCache != null) {
            return recommendByItemFromCache;
        }
        // 从数据库中获取推荐商品
        List<RecommendedItem> recommendedItems = this.recommendUsersByItemCF(itemId, recommendNum);
        List<RecommendUserEntity> productEntities = itemsToEntities(recommendedItems, recommendNum);
        // 缓存推荐商品
        redissonService.setValue(key, productEntities, 12, TimeUnit.HOURS);
        return productEntities;
    }

    protected abstract List<RecommendedItem> recommendUsersByItemCF(Long itemId, int recommendNum) throws ClassNotFoundException, TasteException;

    /**
     * 获取用户推荐实体
     * @param itemId        目标物品ID(被关注用户ID) followee_id
     * @return 推荐实体
     * @throws Exception 异常
     */
    public List<RecommendUserEntity> getRecommendEntityByItemCF(Long itemId) throws Exception {
        return this.getRecommendEntityByItemCF(itemId, 10);
    }


    /**
     * 获取用户推荐：基于关注用户的相似性
     * @param userId          目标用户ID
     * @param topN 推荐数量
     * @param neighborhoodCount 邻居数量
     * @return 推荐用户
     * @throws Exception 异常
     */
    public List<RecommendUserEntity> getRecommendEntityByUserCF(Long userId, int topN, int neighborhoodCount) throws Exception {
        if (userId == null) {
            throw new IllegalArgumentException("userId must not be null");
        }
        if (topN <= 0) {
            throw new IllegalArgumentException("topN must be greater than 0");
        }
        if (neighborhoodCount <= 0) {
            throw new IllegalArgumentException("neighborhoodCount must be greater than 0");
        }

        // 从缓存中获取推荐用户
        String key =  "userId_" + userId + "_" + topN + "_" + neighborhoodCount;
        List<RecommendUserEntity> recommendationsFromCache =  redissonService.getList(key, RecommendUserEntity.class);
        if (recommendationsFromCache != null) {
            return recommendationsFromCache;
        }
        // 缓存未命中，获取推荐商品
        List<RecommendedItem> recommendedItems = this.recommendUsersByUserCF(userId, neighborhoodCount, topN);
        List<RecommendUserEntity> productEntities = itemsToEntities(recommendedItems, topN);
        // 缓存推荐商品
        redissonService.setValue(key, productEntities, 12, TimeUnit.HOURS);
        return productEntities;
    }

    protected abstract List<RecommendedItem> recommendUsersByUserCF(Long userId, int neighborhoodCount, int topN) throws ClassNotFoundException, TasteException;

    public List<RecommendUserEntity> getRecommendEntityByUserCF(Long userId) throws Exception {
        return getRecommendEntityByUserCF(userId, 10, 10);
    }


    private List<RecommendUserEntity> itemsToEntities(List<RecommendedItem> recommendedItems, int topN)  {
        if (recommendedItems.isEmpty()) {
//            return getTopHotProducts(topN);
            return Collections.emptyList();
        }
        // 按照评分排序并取前 topN 个商品
        List<Long> productIds = new LinkedList<>();
        recommendedItems.stream().map(RecommendedItem::getItemID).forEach(productIds::add);
        return  userRepository.queryByIds(productIds);
    }


    /**
     * 获取用户推荐：基于关注用户的相似性
     * @param userId          目标用户ID
     * @param topN 推荐数量
     * @param neighborhoodCount 邻居数量
     * @return 推荐用户
     * @throws Exception 异常
     */
    public List<RecommendUserEntity> getRecommendEntityByHybrid(Long userId, int topN, int neighborhoodCount) throws Exception {
        if (userId == null) {
            throw new IllegalArgumentException("userId must not be null");
        }
        if (topN <= 0) {
            throw new IllegalArgumentException("topN must be greater than 0");
        }
        if (neighborhoodCount <= 0) {
            throw new IllegalArgumentException("neighborhoodCount must be greater than 0");
        }

        // 从缓存中获取推荐用户
        String key =  "hybrid_userId_" + userId + "_" + topN + "_" + neighborhoodCount;
        List<RecommendUserEntity> recommendationsFromCache =  redissonService.getList(key, RecommendUserEntity.class);
        if (recommendationsFromCache != null) {
            return recommendationsFromCache;
        }
        // 缓存未命中，获取推荐商品
        List<RecommendedItem> recommendedItems = this.hybridRecommend(userId, neighborhoodCount, topN);
        List<RecommendUserEntity> productEntities = itemsToEntities(recommendedItems, topN);
        // 缓存推荐商品
        redissonService.setValue(key, productEntities, 12, TimeUnit.HOURS);
        return productEntities;
    }

    protected abstract List<RecommendedItem> hybridRecommend(Long userId, int neighborhoodCount, int topN) throws ClassNotFoundException, TasteException;

    /**
     * 获取用户推荐：基于关注用户的相似性
     * @param userId          目标用户ID
     * @return 推荐用户
     * @throws Exception 异常
     */
    public List<RecommendUserEntity> getRecommendEntityByHybrid(Long userId) throws Exception {

        return getRecommendEntityByHybrid(userId, 10, 10);
    }

}